package com.tools.web.interceptor.limit;

import com.tools.common.object.Force;
import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import com.tools.db.cache.local.LocalCache;
import com.tools.web.interceptor.auth.AuthenticationManager;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 使用 LocalCache 本地缓存实现的 IP-URI 同 IP 请求同一个接口的请求限流拦截器
 * */
@Note("使用 LocalCache 本地缓存实现的 IP-URI 同 IP 请求同一个接口的请求限流拦截器")
public final class LocalCacheRequestLimitInterceptor extends AbsRequestLimitInterceptor {

    @Note("本地缓存实例")
    private final LocalCache cache;

    @Note("拒绝访问结果存储在 HttpServletRequest 里的 name")
    private static final String DENY_NAME = "deny";

    public LocalCacheRequestLimitInterceptor(LocalCache cache, RequestLimitSource source) {
        this(cache, source, null);
    }

    public LocalCacheRequestLimitInterceptor(LocalCache cache, RequestLimitSource source, AuthenticationManager authenticationManager) {
        super(source, authenticationManager);
        this.cache = Objects.requireNonNull(cache, "LocalCache 本地缓存实例不能为 null");
    }

    @Override
    protected boolean protected_checkIsAllow(RequestLimit limit, HttpServletRequest request,
                                             HttpServletResponse response,
                                             AuthenticationManager authenticationManager) throws Exception {
        String key = super.protected_getKey(request);
        long time = limit.time();
        Timeunit timeunit = limit.timeunit();
        long newExpiration = Timeunit.toMill(time, timeunit);
        int allow = limit.allow();
        Force<Integer> forceHandler = new Force<Integer>(allow) {
            @Override
            public Integer force(Integer source) {
                int allow = this.getParam(0, int.class);
                HttpServletRequest request = this.getParam(1, HttpServletRequest.class);
                int newCount = source + 1;
                if(newCount < allow) {
                    return newCount;
                }
                request.setAttribute(DENY_NAME, true);
                return newCount;
            }
        };
        this.cache.set(key, forceHandler, 1, newExpiration);
        if(request.getAttribute(DENY_NAME) == null) {
            return true;
        }
        if(authenticationManager != null) {
            authenticationManager.handleDisable(request);
        }
        response.getWriter().write(super.protected_getLimitErrorMessage(limit));
        return false;
    }
}
